﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ChessAnotator.Base.Business
{
    public class Utilities
    {
        public static T StringToEnum<T>(string name)
        {
            return (T)Enum.Parse(typeof(T), name);
        }

        public static int GetSquareNbr(string sSquareName)
        {
            int coordinates = int.Parse(sSquareName.Substring(2, 2)); // Skip "sq" from the name.
            int nRank = coordinates / 10;
            int nFile = coordinates % 10;

            return GetSquareNbr(nRank, nFile);
        }
        
        public static int GetSquareNbr(
            int nRank
            , int nFile
            )
        {
            if (0 <= nRank && nRank < Nomenclature.BOARD_SIZE
                && 0 <= nFile && nFile < Nomenclature.BOARD_SIZE
                )
            {
                return (nRank * Nomenclature.BOARD_SIZE) + nFile;
            }
            else
            {
                return -1;
            }
        }

        public static int GetSquareRank(int nSquareNbr)
        {
            return nSquareNbr / Nomenclature.BOARD_SIZE;
        }

        public static int GetSquareFile(int nSquareNbr)
        {
            return nSquareNbr % Nomenclature.BOARD_SIZE;
        }

        public static string GetSquareName(int nSquareNbr)
        {
            int nRank = nSquareNbr / Nomenclature.BOARD_SIZE;
            int nFile = nSquareNbr % Nomenclature.BOARD_SIZE;

            return "sq" + nRank.ToString() + nFile.ToString();
        }

        public static string GetSquareName(
            int nRank
            , int nFile
            )
        {
            return "sq" + nRank.ToString() + nFile.ToString();
        }

        public static int GetOffsetedSquareNbr(
            int nOriginalSquareNbr
            , int nRankOffset
            , int nFileOffset
            )
        {
            int nOriginalRank = nOriginalSquareNbr / Nomenclature.BOARD_SIZE;
            int nOriginalFile = nOriginalSquareNbr % Nomenclature.BOARD_SIZE;

            return GetSquareNbr(
                nOriginalRank + nRankOffset
                , nOriginalFile + nFileOffset
                );
        }

        public static bool IsSquareInsideBoard(int nSquareNbr)
        {
            return 0 <= nSquareNbr && nSquareNbr < Nomenclature.BOARD_SIZE * Nomenclature.BOARD_SIZE;
        }

        #region Pieces
        private static List<int> GetBishopAttackedSquares(
            int nCurrSquareNbr
            , int nMaxDepth
            )
        {
            List<int> arrResult = new List<int>();
            int nOffsetedSquareNbr;
            bool bContinueNW = true;
            bool bContinueNE = true;
            bool bContinueSE = true;
            bool bContinueSW = true;

            for (int nWaveLength = 1; nWaveLength <= nMaxDepth; nWaveLength++)
            {
                if (bContinueNE)
                {
                    nOffsetedSquareNbr = GetOffsetedSquareNbr(nCurrSquareNbr, -nWaveLength, nWaveLength);
                    bContinueNE = IsSquareInsideBoard(nOffsetedSquareNbr);
                    if (bContinueNE)
                    {
                        arrResult.Add(nOffsetedSquareNbr);
                    }
                }

                if (bContinueNW)
                {
                    nOffsetedSquareNbr = GetOffsetedSquareNbr(nCurrSquareNbr, -nWaveLength, -nWaveLength);
                    bContinueNW = IsSquareInsideBoard(nOffsetedSquareNbr);
                    if (bContinueNW)
                    {
                        arrResult.Add(nOffsetedSquareNbr);
                    }
                }

                if (bContinueSE)
                {
                    nOffsetedSquareNbr = GetOffsetedSquareNbr(nCurrSquareNbr, nWaveLength, nWaveLength);
                    bContinueSE = IsSquareInsideBoard(nOffsetedSquareNbr);
                    if (bContinueSE)
                    {
                        arrResult.Add(nOffsetedSquareNbr);
                    }
                }

                if (bContinueSW)
                {
                    nOffsetedSquareNbr = GetOffsetedSquareNbr(nCurrSquareNbr, nWaveLength, -nWaveLength);
                    bContinueSW = IsSquareInsideBoard(nOffsetedSquareNbr);
                    if (bContinueSW)
                    {
                        arrResult.Add(nOffsetedSquareNbr);
                    }
                }
            }

            return arrResult;
        }
        public static List<int> GetBishopAttackedSquares(int nCurrSquareNbr)
        {
            return GetBishopAttackedSquares(
                nCurrSquareNbr
                , Nomenclature.BOARD_SIZE - 1
                );
        }

        private static List<int> GetRookAttackedSquares(
            int nCurrSquareNbr
            , int nMaxDepth
            )
        {
            List<int> arrResult = new List<int>();
            int nOffsetedSquareNbr;
            bool bContinueN = true;
            bool bContinueS = true;
            bool bContinueE = true;
            bool bContinueW = true;

            for (int nWaveLength = 1; nWaveLength <= nMaxDepth; nWaveLength++)
            {
                if (bContinueN)
                {
                    nOffsetedSquareNbr = GetOffsetedSquareNbr(nCurrSquareNbr, -nWaveLength, 0);
                    bContinueN = IsSquareInsideBoard(nOffsetedSquareNbr);
                    if (bContinueN)
                    {
                        arrResult.Add(nOffsetedSquareNbr);
                    }
                }

                if (bContinueS)
                {
                    nOffsetedSquareNbr = GetOffsetedSquareNbr(nCurrSquareNbr, nWaveLength, 0);
                    bContinueS = IsSquareInsideBoard(nOffsetedSquareNbr);
                    if (bContinueS)
                    {
                        arrResult.Add(nOffsetedSquareNbr);
                    }
                }

                if (bContinueE)
                {
                    nOffsetedSquareNbr = GetOffsetedSquareNbr(nCurrSquareNbr, 0, nWaveLength);
                    bContinueE = IsSquareInsideBoard(nOffsetedSquareNbr);
                    if (bContinueE)
                    {
                        arrResult.Add(nOffsetedSquareNbr);
                    }
                }

                if (bContinueW)
                {
                    nOffsetedSquareNbr = GetOffsetedSquareNbr(nCurrSquareNbr, 0, -nWaveLength);
                    bContinueW = IsSquareInsideBoard(nOffsetedSquareNbr);
                    if (bContinueW)
                    {
                        arrResult.Add(nOffsetedSquareNbr);
                    }
                }
            }

            return arrResult;
        }
        public static List<int> GetRookAttackedSquares(
            int nCurrSquareNbr
            )
        {
            return GetRookAttackedSquares(
                nCurrSquareNbr
                , Nomenclature.BOARD_SIZE - 1
                );
        }

        public static List<int> GetQueenAttackedSquares(int nCurrSquareNbr)
        {
            List<int> arrResult = new List<int>();
            arrResult.AddRange(GetBishopAttackedSquares(nCurrSquareNbr));
            arrResult.AddRange(GetRookAttackedSquares(nCurrSquareNbr));

            return arrResult;
        }

        public static List<int> GetKingAttackedSquares(int nCurrSquareNbr)
        {
            List<int> arrResult = new List<int>();
            arrResult.AddRange(GetBishopAttackedSquares(nCurrSquareNbr, 1));
            arrResult.AddRange(GetRookAttackedSquares(nCurrSquareNbr, 1));

            return arrResult;
        }

        public static List<int> GetKnightAttackedSquares(int nCurrSquareNbr)
        {
            List<int> arrResult = new List<int>();
            int nOffsetedSquareNbr;

            // North
            nOffsetedSquareNbr = GetOffsetedSquareNbr(nCurrSquareNbr, -2, -1);
            if (IsSquareInsideBoard(nOffsetedSquareNbr))
            {
                arrResult.Add(nOffsetedSquareNbr);
            }
            nOffsetedSquareNbr = GetOffsetedSquareNbr(nCurrSquareNbr, -2, 1);
            if (IsSquareInsideBoard(nOffsetedSquareNbr))
            {
                arrResult.Add(nOffsetedSquareNbr);
            }

            // South
            nOffsetedSquareNbr = GetOffsetedSquareNbr(nCurrSquareNbr, 2, -1);
            if (IsSquareInsideBoard(nOffsetedSquareNbr))
            {
                arrResult.Add(nOffsetedSquareNbr);
            }
            nOffsetedSquareNbr = GetOffsetedSquareNbr(nCurrSquareNbr, 2, 1);
            if (IsSquareInsideBoard(nOffsetedSquareNbr))
            {
                arrResult.Add(nOffsetedSquareNbr);
            }

            // East
            nOffsetedSquareNbr = GetOffsetedSquareNbr(nCurrSquareNbr, 1, 2);
            if (IsSquareInsideBoard(nOffsetedSquareNbr))
            {
                arrResult.Add(nOffsetedSquareNbr);
            }
            nOffsetedSquareNbr = GetOffsetedSquareNbr(nCurrSquareNbr, -1, 2);
            if (IsSquareInsideBoard(nOffsetedSquareNbr))
            {
                arrResult.Add(nOffsetedSquareNbr);
            }

            // West
            nOffsetedSquareNbr = GetOffsetedSquareNbr(nCurrSquareNbr, 1, -2);
            if (IsSquareInsideBoard(nOffsetedSquareNbr))
            {
                arrResult.Add(nOffsetedSquareNbr);
            }
            nOffsetedSquareNbr = GetOffsetedSquareNbr(nCurrSquareNbr, -1, -2);
            if (IsSquareInsideBoard(nOffsetedSquareNbr))
            {
                arrResult.Add(nOffsetedSquareNbr);
            }

            return arrResult;
        }
        #endregion
    }
}
